#include "inet.h"
#include <string.h>

static int sock;
static node_t *players = NULL;

fd_set master;
int max_fd = 0;
uint32_t num_players;
FILE *log_file;

extern uint8_t q_map[WIDTH_AREA][HEIGHT_AREA];

void init_server(const struct sockaddr_in *addr)
{
    int optval = 1;

    sock = Socket(AF_INET, SOCK_STREAM, 0);
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int));
    
    Bind(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in));
    Listen(sock, num_players);

    fprintf(log_file, "[SERVER] Server init\n");
}


void wait_clients()
{
    fd_set read_fds;
    struct sockaddr_in client;
    socklen_t addr_len;
    int new_fd;
    mess_t message;

    uint8_t ready;

    char player_name[256];
    player_serv_t *new_player;
    node_t *iter_pl;
    node_t *iter;

    max_fd = sock;
    FD_ZERO(&master);
    FD_ZERO(&read_fds);

    FD_SET(sock, &master);

    fprintf(log_file, "[SERVER] Waiting %d clients connection...\n", num_players);

    addr_len = sizeof(client);
    ready = 0;

    init_list(&players);
    iter_pl = players;
    while (ready < num_players)
    {
        read_fds = master;    

        Select(max_fd+1, &read_fds, NULL, NULL, NULL);
        for (int fd = 0; fd <= max_fd; ++fd)
        {
            if (FD_ISSET(fd, &read_fds))
            {
                //new client
                if (fd == sock)
                {
                    new_fd = Accept(sock, (struct sockaddr*)&client, &addr_len);

                    if (Recv(new_fd, &message, sizeof(mess_t), 0) == 0)
                    {
                        Close(new_fd);
                        continue;
                    }

                    message.magic = ntohl(message.magic);
                    message.ptype = ntohl(message.ptype);
                    message.datasize = ntohl(message.datasize);

                    if (message.magic != MAGIC || message.ptype != 0x01)
                    {
                        Close(new_fd);
                        continue;
                    }

                    message.datasize = MAX(1, MIN(MAX_NAME_LEN, message.datasize));
                    if (Recv(new_fd, &player_name, message.datasize, 0) == 0)
                    {
                        Close(new_fd);
                        continue;
                    }

                    player_name[message.datasize] = '\0';

                    if (!check_name(player_name))
                    {
                        Close(new_fd);
                        continue;
                    }

                    new_player = malloc(sizeof(player_serv_t));
                    strncpy(new_player->player_name, player_name, message.datasize+1);
                    new_player->player_name_len = message.datasize;
                    new_player->fd = new_fd;

                    iter_pl = insert_node(iter_pl, new_player);
                    max_fd = MAX(new_fd, max_fd);
                    
                    FD_SET(new_fd, &master);

                    send_map(new_fd);

                    fprintf(log_file, "[SERVER] Client %d connected success from %s:%d, file desc: %d\n", 
                            ready+1, inet_ntoa(client.sin_addr), ntohs(client.sin_port), new_fd);
                }
                //user ready
                else
                {
                    ready += 1;
                    if (Recv(fd, &message, sizeof(mess_t), 0) == 0)
                    {
                        Close(fd);
                        FD_CLR(fd, &master);
                        ready -= 1;
                        
                        iter = players->next;
                        while (iter != NULL)
                        {
                            if (((player_serv_t *)(iter->data))->fd == fd)
                            {
                                delete_name(((player_serv_t *)(iter->data))->player_name, &iter_pl);
                                break; 
                            }           
                            iter = iter->next;
                        }

                        fprintf(log_file, "[SERVER] Client disconnect, file desc: %d\n", fd);

                        continue;
                    }

                    message.magic = ntohl(message.magic);
                    message.ptype = ntohl(message.ptype);
                    message.datasize = ntohl(message.datasize);

                    if (message.magic != MAGIC || message.ptype != 0x02)
                    {
                        Close(fd);
                        FD_CLR(fd, &master);
                        ready -= 1;

                        iter = players->next;
                        while (iter != NULL)
                        {
                            if (((player_serv_t *)(iter->data))->fd == fd)
                            {
                                delete_name(((player_serv_t *)(iter->data))->player_name, &iter_pl);
                                break;
                                
                            }           
                            iter = iter->next;
                        }

                        fprintf(log_file, "[SERVER] Client sended incorrect data, file desc: %d\n", fd);
                    }
                }
            }
        }
    }

}


void delete_name(char *name, node_t **last)
{
    node_t *iter;

    iter = players->next;
    while (iter != NULL)
    {
        if (strcmp(((player_serv_t *)iter->data)->player_name, name) == 0)
        {
            fprintf(log_file, "[SERVER] Name '%s' was deleted\n", name);
            delete_node(iter, last);
            break;
        }

        iter = iter->next;
    }
}

int check_name(char *name)
{
    node_t *iter;
    
    iter = players->next;
    while (iter != NULL)
    {
        if (strcmp(((player_serv_t *)iter->data)->player_name, name) == 0)
        {
            fprintf(log_file, "[SERVER] Name '%s' taken\n", name);
            return 0;
        }
        iter = iter->next;
    }

    fprintf(log_file, "[SERVER] New name '%s'\n", name);
    return 1;
} 


void send_map(int fd)
{
    mess_t message;
    message.magic = htonl(MAGIC);
    message.ptype = htonl(0x10);
    message.datasize = htonl(HEIGHT_AREA*WIDTH_AREA);
    
    Send(fd, &message, sizeof(mess_t), 0);
    Send(fd, &q_map, HEIGHT_AREA*WIDTH_AREA, 0);
    
    fprintf(log_file, "[SERVER] Send map to file desc: %d\n", fd);
}


void start_game(uint32_t frame_delay, crd_t player)
{
    mess_t message = {0};
    node_t *iter;
    node_t *iter_pl;

    uint32_t dir;
    uint32_t net_num_players;
    uint32_t data_size;
 
    player_serv_t *pl_i;
    player_serv_t *pl_j;
    player_t net_client_player;

    crd_t players_crd[4];

    frame_delay = htonl(frame_delay);
    net_num_players = htonl(num_players);

    gen_start_crd(player, players_crd);

    message.magic = htonl(MAGIC);
    message.ptype = htonl(0x20);

    message.datasize = message.datasize + sizeof(frame_delay) + sizeof(num_players) + sizeof(uint32_t)*4*num_players;
    iter_pl = players->next;
    while(iter_pl != NULL)
    {
        pl_i = (player_serv_t *)iter_pl->data;
        message.datasize += pl_i->player_name_len;

        iter_pl = iter_pl->next;
    }

    message.datasize = htonl(message.datasize);

    iter_pl = players->next;
    for (int i = 0; i < num_players; ++i, iter_pl = iter_pl->next)
    {
        dir = RIGHT;
        pl_i = (player_serv_t *)iter_pl->data;
        Send(pl_i->fd, &message, sizeof(mess_t), 0);
        Send(pl_i->fd, &frame_delay, sizeof(uint32_t), 0);
        Send(pl_i->fd, &net_num_players, sizeof(uint32_t), 0);

        iter = players->next;
        for (int j = 0; j < num_players; ++j, iter = iter->next)
        {
            pl_j = (player_serv_t *)iter->data;

            net_client_player.start_x = htonl(players_crd[j].x);
            net_client_player.start_y = htonl(players_crd[j].y);
            net_client_player.start_direction = htonl(dir);
            net_client_player.player_name_len = htonl(pl_j->player_name_len);
            strncpy(net_client_player.player_name, pl_j->player_name, pl_j->player_name_len+1);

            data_size = sizeof(uint32_t)*4 + pl_j->player_name_len;
            Send(pl_i->fd, &net_client_player, data_size, 0);

            dir = (dir + 2) % 4;
        }
    }

    FD_CLR(sock, &master);
}


int name_to_fd(char *name)
{
    node_t *iter;
    player_serv_t *pl;

    iter = players->next;
    while (iter != NULL)
    {
        pl = (player_serv_t *)iter->data;
        if (strcmp(pl->player_name, name) == 0)
            return pl->fd;
    }

    return -1;
}


void disconnect(int fd)
{
    node_t *iter_pl;

    iter_pl = players->next;
    while (iter_pl != NULL)
    {
        if (((player_serv_t *)iter_pl->data)->fd == fd)
        {
            fprintf(log_file, "[SERVER] Disconected client '%s'\n", fd_to_name(fd));
            Close(fd);
            delete_node(iter_pl, NULL);
            FD_CLR(fd, &master);
            break;
        }

        iter_pl = iter_pl->next;
    }
}


void send_new_state(int fd, uint8_t dir)
{
    mess_t message;
    node_t *iter;
    player_serv_t *pl;
    char *name;

    uint32_t name_size;

    name = fd_to_name(fd);
    name_size = strlen(name);

    message.magic = htonl(MAGIC);
    message.ptype = htonl(-1);
    message.datasize = htonl(1 + name_size);

    iter = players->next;
    while (iter != NULL)
    {
        pl = (player_serv_t *)iter->data;

        if (strcmp(pl->player_name, name) == 0)
            goto NEXT_ITER;

        Send(pl->fd, &message, sizeof(mess_t), 0);
        Send(pl->fd, &dir, 1, 0);
        Send(pl->fd, name, name_size, 0);

    NEXT_ITER:
        iter = iter->next;
    }
}


char *fd_to_name(int fd)
{
    node_t *iter;
    player_serv_t *pl;

    iter = players->next;
    while (iter != NULL)
    {
        pl = (player_serv_t *)iter->data;
        if (pl->fd == fd)
            return pl->player_name;
        
        iter = iter->next;
    }

    return NULL;
}
